package jmine.tec.web.servlet.filter;

import java.io.IOException;
import java.util.concurrent.Future;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import jmine.tec.web.servlet.util.ServletExecutor;
import bancosys.tec.utils.reflection.ReflectionUtils;

/**
 * Filter que chama o {@link Filter#init(FilterConfig)} em uma thread separada
 * 
 * @author takeshi
 */
public class LazyInitializingFilter implements Filter {

    private String filterClassName;

    private Future<?> future;

    private volatile Filter delegate = new NoOpFilter();

    private FilterConfig config;

    /**
     * {@inheritDoc}
     * 
     * @see javax.servlet.Filter#destroy()
     */
    public void destroy() {
        this.delegate.destroy();
        if (this.future != null) {
            this.future.cancel(true);
        }
        ServletExecutor.shutdownExecutorServices(this.config.getServletContext(), false);
    }

    /**
     * {@inheritDoc}
     */
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        this.delegate.doFilter(request, response, chain);
    }

    /**
     * {@inheritDoc}
     */
    public void init(FilterConfig filterConfig) throws ServletException {
        this.config = filterConfig;
        this.filterClassName = filterConfig.getInitParameter("filterClassName");
        this.delegate.init(filterConfig);
        try {
            @SuppressWarnings("unchecked")
            Class<? extends Filter> filterType = (Class<? extends Filter>) ReflectionUtils.findClass(this.filterClassName);
            final Filter instance = filterType.getConstructor().newInstance();
            this.future =
                    ServletExecutor.getOrCreateExecutorService(filterConfig.getServletContext())
                    .submit(new FilterInitializerTask(instance));
        } catch (Exception e) {
            throw new ServletException(e);
        }
    }

    /**
     * @author takeshi
     */
    private final class FilterInitializerTask implements Runnable {
        private final Filter instance;

        /**
         * @param instance {@link Filter}
         */
        private FilterInitializerTask(Filter instance) {
            this.instance = instance;
        }

        /**
         * {@inheritDoc}
         */
        public void run() {
            try {
                this.instance.init(LazyInitializingFilter.this.config);
                LazyInitializingFilter.this.delegate = this.instance;
            } catch (ServletException e) {
                throw new RuntimeException();
            }
        }
    }

}
